﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Timers;
using System.Windows.Forms;

namespace Snake
{
	/// <summary>
	/// A kígyót reprezentáló osztály
	/// </summary>
	public class snakeObject : gameObject
	{
		/// <summary>
		/// A kígyó testét láncolt listaként foghatjuk fel, aminek az első eleme maga a kígyó feje
		/// </summary>
		protected LinkedList<snakeBody> bodies;

		/// <summary>
		/// Ez a változó tárolja, hogy halott-e a kígyó.
		/// </summary>
		protected bool isdead = false;

		/// <summary>
		/// Egy getter/setter függvény, amivel lekérhetjük a kígyó "egészségügyi állapotát".
		/// Ha a kígyó meghal, akkor ez a függvény hívja meg a DieEvent eseményt.
		/// </summary>
		public bool isDead
		{
			get
			{
				return isdead;
			}
			set
			{
				if (isdead = value && DieEvent != null)
				{
					DieEvent(this);
				}
			}
		}

		/// <summary>
		/// A kígyó halálakor ilyen típusú függvényt hívhatunk meg.
		/// </summary>
		/// <param name="snake">A meghalt kígyót reprezentálja</param>
		public delegate void DieDelegate(snakeObject snake);

		/// <summary>
		/// A kígyó halálakor ez az esemény fut le.
		/// </summary>
		public event DieDelegate DieEvent;

		/// <summary>
		/// Konstruktor
		/// </summary>
		public snakeObject()
		{
			bodies = new LinkedList<snakeBody>();

			snakeBody newOne = new snakeBody() { direction = new Point(1, 0), position = new Point(10, 10), size = new Size(1, 1) };
			bodies.AddFirst(newOne);

			grow(5);
		}

		/// <summary>
		/// A kígyó mozgásáért felelős eljárás. Ez mozgatja a kígyó testét a feje irányába.
		/// </summary>
		public void move()
		{
			if (bodies.First != null)
			{ 
				snakeBody body = bodies.First.Value.Clone() as snakeBody;

				body.position = new Point(body.position.X + body.direction.X * body.size.Width,
										body.position.Y + body.direction.Y * body.size.Height);
				bodies.AddFirst(body);

                moved = false;
			}
			if (bodies.Count > 0)
			{
				bodies.RemoveLast();
			}
		}

		/// <summary>
		/// Ezzel a függvénnyel lehet, növelni a kígyó hosszát.
		/// </summary>
		public void grow()
		{
			snakeBody newOne = new snakeBody();
			newOne.direction = bodies.Last.Value.direction;
			newOne.position = bodies.Last.Value.position;
			newOne.position = new Point(newOne.position.X - newOne.direction.X * newOne.size.Width,
										newOne.position.Y - newOne.direction.Y * newOne.size.Height);
			bodies.AddLast(newOne);
		}

		/// <summary>
		/// Rekurzív függvény, mely n-szer meghívja a grow() függvényt.
		/// </summary>
		/// <param name="n">n-vel növeli a kígyó hosszát</param>
		public void grow(int n)
		{
			if (n > 0)
			{
				grow();
				grow(n - 1);
			}
		}

		/// <summary>
		/// Ez a függvény ellenőrzi, hogy a kígyó nekiment-e saját magának
		/// </summary>
		/// <returns>Igazzal tér vissza, ha megtörtént az ütközés.</returns>
		public bool isIntersected()
		{
			snakeBody first = bodies.First.Value;
			Point first_point = new Point(first.position.X + first.direction.X, 
										  first.position.Y + first.direction.Y);

			LinkedListNode<snakeBody> p = bodies.First.Next;

			while (p != null && p.Value.position != first.position)
			{
				p = p.Next;
			}

			return (p != null ? p.Value.position : Point.Empty) == first.position;
		}

		/// <summary>
		/// Ez a függvény ellenőrzi, hogy a kígyó ütközött-e az függvénynek átadott gameObject-el.
		/// </summary>
		/// <param name="obj">A megvizsgálandó játékobjektum</param>
		/// <returns>Igazzal tér vissza, ha megtörtént az ütközés</returns>
		public bool isIntersected(gameObject obj)
		{
            Point p = new Point(bodies.First.Value.position.X - bodies.First.Value.direction.X,
                              bodies.First.Value.position.Y - bodies.First.Value.direction.Y);

            bool L = obj != null && bodies.First.Value.position == obj.position;

			return L;
            //return obj != null && p == obj.position;
		}

        private bool moved = false;

		/// <summary>
		/// A kígyó irányításáért felelős eljárás.
		/// </summary>
		/// <param name="sender">A küldő referenciáját itt kapja meg az eljárás</param>
		/// <param name="e">A lenyomott gombok adatait ezen keresztül kapja meg az eljárás</param>
		public void ManageControl(object sender, KeyEventArgs e)
		{
			Point direction = bodies.First.Value.direction;

            if (!moved)
            {
                if (e.KeyCode == Keys.Down && direction != new Point(0, 1) && direction != new Point(0, -1))
                {
                    bodies.First.Value.direction = new Point(0, 1);
                    moved = true;
                }
                else if (e.KeyCode == Keys.Up && direction != new Point(0, -1) && direction != new Point(0, 1))
                {
                    bodies.First.Value.direction = new Point(0, -1);
                    moved = true;
                }
                else if (e.KeyCode == Keys.Left && direction != new Point(-1, 0) && direction != new Point(1, 0))
                {
                    bodies.First.Value.direction = new Point(-1, 0);
                    moved = true;
                }
                else if (e.KeyCode == Keys.Right && direction != new Point(1, 0) && direction != new Point(-1, 0))
                {
                    bodies.First.Value.direction = new Point(1, 0);
                    moved = true;
                }
            }
		}

		/// <summary>
		/// A kígyó kirajzolásáért felelős eljárás.
		/// </summary>
		/// <param name="g">Ezen adjuk át, hogy hova rajzoljon az eljárás.</param>
		public override void Paint(Graphics g)
		{
			if (g != null)
			{
				foreach (gameObject obj in bodies)
				{
					obj.Paint(g);
				}
			}
		}
	}
}